#ifndef __SINK_HPP__
#define __SINK_HPP__

#include <iostream>
#include <fstream>
#include <sstream>
#include <cassert>
#include <memory>
#include <ctime>
#include "Until.hpp"

namespace Logs
{
    class Sink
    {
    public:
        using Ptr=std::shared_ptr<Sink>;
        virtual ~Sink(){}
        virtual void log(const char* str,size_t len) = 0;
    };
    enum class Time_Gap
    {
        GAP_DAY,
        GAP_HOUR,
        GAP_MINUTE,
        GAP_SECOND
    };
    // 按时间进行滚动。
    class RollByTimeSink : public Sink
    {
    public:
        RollByTimeSink(const std::string& base_filename,Time_Gap gap)
            :_base_filename(base_filename)
        {
            switch(gap)
            {
                case Time_Gap::GAP_DAY : _gap_size = 3600 * 24 ;break;
                case Time_Gap::GAP_HOUR : _gap_size = 3600 ;break;
                case Time_Gap::GAP_MINUTE : _gap_size = 60 ;break;
                case Time_Gap::GAP_SECOND : _gap_size = 1 ;break;
            }
            // 当前是第几段。
            _cur_gap = time(nullptr) % _gap_size;
            Until::File::createdirectory(Until::File::path(_base_filename));
        }
        void log(const char* str,size_t len) override
        {
            time_t cur = Until::Time::GetTime();
            std::string filename;
            if(cur % _gap_size == _cur_gap)
            {
                filename = CreateFile();
            }
            _ofs.close();
            _ofs.open(filename,std::ios::binary | std::ios::app);
            assert(_ofs.good());
            _ofs.write(str,len);
        }
        std::string CreateFile()
        {
            time_t t = Until::Time::GetTime();
            struct tm lt;
            localtime_r(&t,&lt);
            std::stringstream ss;
            ss << _base_filename
            << lt.tm_year +1900
            << lt.tm_mon + 1
            << lt.tm_mday
            << lt.tm_hour
            << lt.tm_min
            << lt.tm_sec
            << ".log";

            return ss.str();
        }
    private:
        std::string _base_filename;
        std::ofstream _ofs;
        size_t _gap_size; // 时间段大小。
        size_t _cur_gap;  // 第几个时间段。
    };
    // 直接输出到屏幕上。
    class StdoutSink : public Sink
    {
    public:
        void log(const char* str,size_t len) override
        {
            std::cout.write(str,len);
        }
    };
    // 输出到文件里。
    class FileSink : public Sink
    {
    public:
        // 打开就关下次还要再打开，太麻烦了。
        FileSink(const std::string& pathname)
            :_pathname(pathname)
        {
            // 1.创建文件所在目录。
            Until::File::createdirectory(Until::File::path(_pathname));
            // 2.创建并打开文件。
            _ofs.open(_pathname,std::ios::binary | std::ios::app);
            // 3. 判断文件是否被打开。
            assert(_ofs.good());
        }
        void log(const char* str,size_t len) override
        {
            _ofs.write(str,len);
        }
    private:
        std::string _pathname;
        std::ofstream _ofs;
    };
    // 输出到滚动文件里。
    // 按照大小滚动。
    class RollBySizeSink : public Sink
    {
    public:
        RollBySizeSink(const std::string& base_filename,const size_t& max_fsize)
            :_base_filename(base_filename)
            ,_max_fsize(max_fsize)
            ,_cur_fsize(0)
        {
            Until::File::createdirectory(Until::File::path(base_filename));
            //_ofs.open(CreateFile(), std::ios::binary | std::ios::app);
            //assert(_ofs.good());
        }
        void log(const char* str,size_t len) override
        {
            if(_cur_fsize >= _max_fsize)
            {
                _ofs.close();
                _ofs.open(CreateFile(), std::ios::binary | std::ios::app);
                assert(_ofs.good());
                _cur_fsize = 0;
            }
            _ofs.write(str,len);
            _cur_fsize += len;
        }
        std::string CreateFile()
        {
            time_t t = Until::Time::GetTime();
            struct tm lt;
            localtime_r(&t,&lt);
            std::stringstream ss;
            ss << _base_filename
            << lt.tm_year + 1900
            << lt.tm_mon + 1
            << lt.tm_mday
            << lt.tm_hour
            << lt.tm_min
            << lt.tm_sec
            << "-"
            << _name_count++
            << ".log";

            return ss.str();
        }
    protected:
        // 防止多种相同的文件。
        int _name_count;
        std::string _base_filename;
        std::ofstream _ofs;
        size_t _max_fsize;
        size_t _cur_fsize;
    };
    // // 按时间进行滚动。
    // // 直接继承按时间进行滚动，但是滚动就不单一了。
    // class RollByTimeSink : public RollBySizeSink
    // {
    // public:
    //     RollByTimeSink(const std::string& base_filename,const size_t& max_fsize,Time_Gap gap)
    //         :RollBySizeSink(base_filename,max_fsize)// 在初始化列表中初始化父类。
    //     {
    //         switch(gap)
    //         {
    //             case Time_Gap::GAP_DAY : _gap = 3600 * 24 ;break;
    //             case Time_Gap::GAP_HOUR : _gap = 3600 ;break;
    //             case Time_Gap::GAP_MINUTE : _gap = 60 ;break;
    //             case Time_Gap::GAP_SECOND : _gap = 1 ;break;
    //         }
    //     }
    //     void log(const char* str,size_t len)
    //     {
    //         RollBySizeSink::log(str,len);
    //     }
    //     void CreateFile()
    //     {
    //         time_t cur = time(nullptr);
    //         if(cur % _gap == 0)
    //         {
    //             ++Time_Size;
    //             RollBySizeSink::CreateFile();
    //         }
    //     }
    // private:
    //     // std::string _base_filename;
    //     // std::ofstream _ofs;
    //     size_t _gap;
    //     size_t Time_Size;
    // };
    // 制造对应的结构体对象。
    class SinkFactory
    {
    public:
        // 显示化传递。
        template< class SinkType , class ...Args >
        static Sink::Ptr CreateSink(Args&& ...args)
        {
            // 参数包的内存。
            // sizeof ...(args);
            // 传入函数后再展开。
            return std::make_shared<SinkType>(std::forward<Args>(args)...);
        }
    private:
        ;
    };
}

#endif